import java.awt.*;
import javax.swing.Timer;
import java.awt.geom.*;
import java.awt.image.BufferedImage; 
import java.io.BufferedWriter;
import java.io.File; 
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException; 
import java.util.ArrayList;
import java.util.Scanner;
import java.util.logging.Level; 
import java.util.logging.Logger; 
import javax.imageio.ImageIO; 
import javax.swing.JPanel; 
import java.applet.*;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.Timer;
import javax.swing.Action;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class Main extends Applet implements KeyListener {
			
	private Timer timer;
	
	private Thing map[][];
	
	private AbstractFactory factory;
	
	private int timeout = 2;
	
	private final int cellSize = 30;
	
	private ArrayList<Monster> monsterList = new ArrayList<Monster>();	  
	
	private boolean firstRun = true;
		
	private int score = 0;
	
	private boolean b = false;
	
	private Pacman pacman1;
	
	private final int randomMonsterCode = 4;
	
	private final int randomMonsterWithEyesCode = 5;
	
	private final int smartMonsterCode = 6;
	
	private final int pointCode = 2;
	
	private final int cherryCode = 3;
	
	private final int pacmanCode = 9;
			
	private final int wallCode = 1;
	
	private final int delay = 200;

	private static final long serialVersionUID = 4902057285471885308L;
		
	/**
	 * frame init
	 */
	public void init() {
		this.setFocusable(true);
		int startPX = 0;
	    int startPY = 0;
		super.init();
		setBackground(Color.white);
        this.addKeyListener(this);
        Scanner mazeScanner;
		try {
			mazeScanner = new Scanner(new File("maze.txt"));
			int dimensionX = mazeScanner.nextInt();
			int dimensionY = mazeScanner.nextInt();
			this.setSize(dimensionX * cellSize, dimensionY * cellSize);
			map = new Thing[dimensionX][dimensionY];
			for (int i = 0; i < dimensionX; i ++) {
				for (int j = 0; j < dimensionY; j ++) {
					boolean isMonster = false;
					boolean isWallOrBonus = false;
					int a = mazeScanner.nextInt();
					switch (a) {
					case wallCode:
						factory = new WallFactory();
						isWallOrBonus = true;
						break;
					case pointCode:
						factory = new PointFactory();
						isWallOrBonus = true;
						break;
					case cherryCode:
						factory = new CherryFactory();
						isWallOrBonus = true;
						break;	
					case randomMonsterCode: 
		            	factory = new RandomStrategyFactory();
		            	isMonster = true;
		            	break;
					case randomMonsterWithEyesCode:
						factory = new RandomStrategyWithEyesFactory();
		            	isMonster = true;
		            	break;
					case smartMonsterCode:
						factory = new SmartStrategyFactory();
		            	isMonster = true;
		            	break;
					case pacmanCode:
						startPX = i;
						startPY = j;
					break;
		            	
					}
					if (isMonster) {
					monsterList.add((Monster)factory.create(i, j)); };
					if (isWallOrBonus){
						map[i][j] = factory.create(i, j);
						
					}
				}
			}
			
        } catch (FileNotFoundException e) {
			e.printStackTrace();
		}
        pacman1 = new Pacman(startPX, startPY, map);         
        for (Monster currentMonster: monsterList) {
        	currentMonster.init(map, pacman1);
        }
                    
        final Graphics g = getGraphics();
        
        timer = new Timer(delay, new ActionListener() {
        	public void actionPerformed(ActionEvent event) {
        		
        			pacman1.actionPerformed(event);
                paint(g);
                
                timeout++;
           	}
        });
        timer.start();
    }
	    
	/**
	 * drawing method
	 */
	public void paint(final Graphics g) {
		this.showStatus("Score: " + score);
    	this.checkState();
		if (firstRun) {
       		for (int i = 0; i < map.length; i ++) {
       		   for  (int j = 0; j < map[0].length; j ++) {
   					if (map[i][j] != null) {
   						map[i][j].draw(g);
   					}
       		   }
       		}
       		firstRun = false;
       	}
       	pacman1.hide(g);
    	if (timeout == 2) {
    		for (Monster currentMonster: monsterList) {
    			currentMonster.hide(g);
    		}
    	}
    	pacman1.draw(g);	
		if (timeout == 2) {
    		timeout = 0;
    		for (Monster currentMonster: monsterList) {
    			currentMonster.draw(g);
    		}	
    	}
		if ((map[pacman1.getX()][pacman1.getY()] != null) && (map[pacman1.getX()][pacman1.getY()].getClass() == Cherry.class)) {
    		score += cellSize;
    		map[pacman1.getX()][pacman1.getY()] = null;
    	}
    	if ((map[pacman1.getX()][pacman1.getY()] != null) && (map[pacman1.getX()][pacman1.getY()].getClass() == Point.class)) {
    		score += 10;
    		map[pacman1.getX()][pacman1.getY()] = null;
    	}
	}
	
	private void checkState() {
		boolean isEmpty = true;
       	for (int i = 0; ((i < map.length) && (isEmpty == true)); i++) {
    		for (int j = 0; j < map.length; j++) {
    			if ((map[i][j] != null) && (map[i][j].getClass() != Wall.class)) {
    				isEmpty = false;
    				break;
    			}
    		}
    	}
       	if (isEmpty) { 
       		showResults("Congratulations!");
       	}
    	for (Monster currentMonster: monsterList) {
    		if (currentMonster.ifFail(pacman1.getX(), pacman1.getY()))
    			showResults("Failed!");
        }
	}
	
	private void showResults(String state) {
		timer.stop();
		Graphics g = getGraphics();
		g.setColor(Color.BLACK);
		g.drawString(state, (map.length / 2 - 2) * cellSize, 15);
		g.drawString("Your score: " + score, (map.length / 2 - 2) * cellSize, 27);
		showRecordTable();
	}
	
	private void showRecordTable() {
		Scanner recordScanner;
		Integer previousRecord  = 0;
		try {
			recordScanner = new Scanner(new File("records.txt"));
			previousRecord = recordScanner.nextInt();
			recordScanner.close();
		} catch (FileNotFoundException e) {}
		Graphics g = getGraphics();
		if (previousRecord < score) {
			previousRecord = score;
			File source = new File("records.txt");
			try {
				FileWriter writer = new FileWriter(source);
				BufferedWriter buffered = new BufferedWriter(writer);
				buffered.write(previousRecord.toString());
				buffered.flush();
				writer.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		g.drawString("Best result: " + previousRecord, (map.length / 2 - 2) * cellSize, (map[0].length) * cellSize - 10);
		
	}
	
	public void keyPressed(KeyEvent arg0) {
		
		
	}
	
	public void keyReleased(KeyEvent arg0) {
		
		pacman1.keyReleased(arg0);
	}
	
	public void keyTyped(KeyEvent arg0) {
		
		
	}   
}
